package xyz.xtt.asset.service.impl;

import static java.util.stream.Collectors.toList;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.tongtong.middleware.vo.params.MetaDataParams;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.asset.api.enums.AssetTypeEnum;
import xyz.xtt.asset.api.enums.BizTypeEnum;
import xyz.xtt.asset.api.enums.OwnerTypeEnum;
import xyz.xtt.asset.api.enums.ReferProtocolEnum;
import xyz.xtt.asset.api.enums.SecondEnum;
import xyz.xtt.asset.api.enums.ShowStateEnum;
import xyz.xtt.asset.api.enums.StatusAllEnum;
import xyz.xtt.asset.client.request.MintDataVo;
import xyz.xtt.asset.client.request.MintRequest;
import xyz.xtt.asset.client.request.ReMintRequest;
import xyz.xtt.asset.client.response.AssetVo;
import xyz.xtt.asset.client.response.MintResponse;
import xyz.xtt.asset.constants.AssetCache;
import xyz.xtt.asset.constants.ErrorMessage;
import xyz.xtt.asset.entity.TblAsset;
import xyz.xtt.asset.entity.TblAssetExt;
import xyz.xtt.asset.entity.TblAssetStaticResource;
import xyz.xtt.asset.entity.TblAssetTransaction;
import xyz.xtt.asset.entity.TblRemintAccount;
import xyz.xtt.asset.enums.AccountStateEnum;
import xyz.xtt.asset.enums.OrderTypeEnum;
import xyz.xtt.asset.mapper.TblRemintAccountMapper;
import xyz.xtt.asset.service.IInnerService;
import xyz.xtt.asset.service.INftService;
import xyz.xtt.asset.service.ITblAssetAccountService;
import xyz.xtt.asset.service.ITblAssetCardService;
import xyz.xtt.asset.service.ITblAssetExtService;
import xyz.xtt.asset.service.ITblAssetNoService;
import xyz.xtt.asset.service.ITblAssetService;
import xyz.xtt.asset.service.ITblAssetStaticResourceService;
import xyz.xtt.asset.service.ITblAssetTransactionService;
import xyz.xtt.asset.service.ITblRemintAccountService;
import xyz.xtt.asset.utils.BigDecimalUtils;
import xyz.xtt.asset.vo.RemintAccountVo;
import xyz.xtt.common.asset.AssertUtils;
import xyz.xtt.common.starter.cache.CacheManager;
import xyz.xtt.pay.api.enums.PayOrderStatusEnum;
import xyz.xtt.pay.client.response.TblPayOrderDto;

/**
 * <p>
 * 铸作权帐户表 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-02-25
 */
@Slf4j
@Service
public class TblRemintAccountServiceImpl extends ServiceImpl<TblRemintAccountMapper, TblRemintAccount> implements ITblRemintAccountService {
	private static final String FOR_UPDATE = "for update";
	@Autowired
	private IInnerService iInnerService;
	@Autowired
	private ITblAssetTransactionService iTblAssetTransactionService;
	@Autowired
	private ITblAssetExtService iTblAssetExtService;
	@Autowired
	private ITblAssetService iTblAssetService;
	@Autowired
	private ITblAssetCardService iTblAssetCardService;
	@Autowired
	private ITblAssetStaticResourceService iTblAssetStaticResourceService;
	@Autowired
	private INftService iNftService;
	@Autowired
	private ITblAssetAccountService iTblAssetAccountService;
	@Autowired
	private CacheManager cacheManager;
	@Autowired
	private ITblAssetNoService iTblAssetNoService;

	@Override
	public TblRemintAccount initRemintAccountAmount(Long assetId, Long userId, ReferProtocolEnum referProtocol, BigDecimal amount) {
		TblRemintAccount assetAccount = TblRemintAccount
				.builder()
				.id(IdUtil.getSnowflakeNextId())
				.accountNo(IdUtil.getSnowflakeNextId())
				.assetId(assetId)
				.ownerId(userId)
				.amount(amount)
				.state(AccountStateEnum.NORMAL.code())
				.updateTime(LocalDateTime.now())
				.createTime(LocalDateTime.now())
				.ver(0L)
				.referProtocol(referProtocol.getCode())
				.build();
		if (BigDecimalUtils.isGreater(amount)) {
			assetAccount.setVer(1L);
		}
		Boolean init = this.save(assetAccount);
		if (init) {
			return assetAccount;
		}
		return null;
	}

	// 调户调整
	@Override
	public Boolean accountAdjust(Long assetId, Long ownerId, ReferProtocolEnum referProtocol, BigDecimal amount) {
		AssertUtils.isNull(amount, ErrorMessage.REMAINT_ACCOUNT_STATE_EX);
		LambdaQueryChainWrapper<TblRemintAccount> query = new LambdaQueryChainWrapper<>(baseMapper);
		TblRemintAccount remintAccount = query.eq(TblRemintAccount::getAssetId, assetId).eq(TblRemintAccount::getOwnerId, ownerId).last(FOR_UPDATE).one();
		if (Objects.isNull(remintAccount)) {
			if (amount.compareTo(BigDecimal.ZERO) < 0) {
				// 说明是转出方 转出帐户不能空
				AssertUtils.isNull(remintAccount, ErrorMessage.REMAINT_NOT_EXIST);
			} else {
				TblRemintAccount initRemintAccountAmount = initRemintAccountAmount(assetId, ownerId, referProtocol, amount);
				AssertUtils.isNull(initRemintAccountAmount, ErrorMessage.REMAINT_OPERATOR_FAIL);
				return Boolean.TRUE;
			}
		}
		AssertUtils.isTrue(remintAccount.isLock(), ErrorMessage.REMAINT_ACCOUNT_STATE_EX);
		BigDecimal before = remintAccount.getAmount();
		BigDecimal after = before.add(amount);
		AssertUtils.isTrue(BigDecimal.ZERO.compareTo(after) > 0, ErrorMessage.INSUFFICIENT_BALANCE);
		LambdaUpdateChainWrapper<TblRemintAccount> update = new LambdaUpdateChainWrapper<>(baseMapper);
		boolean updateResult = update
				.eq(TblRemintAccount::getAssetId, assetId)
				.eq(TblRemintAccount::getOwnerId, ownerId)
				.set(TblRemintAccount::getAmount, after)
				.setIncrBy(TblRemintAccount::getVer, 1)
				.update();
		AssertUtils.isFalse(updateResult, ErrorMessage.REMAINT_OPERATOR_FAIL);
		return updateResult;
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void reMintPayAfter(TblPayOrderDto payOrderDto) {
		Long payOrderId = payOrderDto.getPayOrderId();
		TblPayOrderDto payOrder = iInnerService.fetchPayOrder(payOrderId);
		if (Objects.isNull(payOrder)) {
			log.info("payOrder is null msg:{}", JSONUtil.toJsonStr(payOrderDto));
			return;
		}
		if (!Objects.equals(payOrder.getPayStatus(), PayOrderStatusEnum.PAY_SUCCESS.getCode())) {
			log.info("payOrder status exception null msg:{}", JSONUtil.toJsonStr(payOrderDto));
			return;
		}
		String businessRefId = payOrder.getBusinessRefId();
		TblAssetTransaction transaction = iTblAssetTransactionService.getById(Longs.tryParse(businessRefId));
		if (Objects.isNull(transaction)) {
			log.info("payOrder transaction is null msg:{}", JSONUtil.toJsonStr(payOrderDto));
			return;
		}
		BigDecimal amount = transaction.getAmount();
		String sellerUserId = transaction.getSellerUserId();
		Long buyerUserId = payOrderDto.getAppUserId();
		Long assetId = transaction.getAssetId();
		TblAssetExt assetExt = iTblAssetExtService.infoWithAssetIdByCache(assetId);
		AssertUtils.isNull(assetExt, ErrorMessage.ASSET_NOT_EXIST);
		ReferProtocolEnum referProtocol = assetExt.referProtocol();
		AssertUtils.isNull(referProtocol, ErrorMessage.UPSUPPORTED_PROTOCOL_TYPE);
		// 调整卖出方的铸作权数量
		accountAdjust(assetId, buyerUserId, referProtocol, amount);
		// 第一售卖时,出售人是没有卡可以作废的
		TblAsset asset = iTblAssetService.assetInfoWithGoodsId(transaction.getGoodsId());
		AssertUtils.isNull(assetExt, ErrorMessage.ASSET_NOT_EXIST);
		// 如果卖出人是作者，不执行作废逻辑 因为此时奢作权没有产生卡片
		Integer secondType = asset.getSecondType();
		Integer ownerType = asset.getOwnerType();
		boolean bool = Objects.equals(secondType, SecondEnum.MINT_POWER.getCode()) && Objects.equals(ownerType, OwnerTypeEnum.USER.code());
		log.info("reMintPayAfter check assetId:{},payOrderId:{},bool:{}", asset.getId(), payOrderId, bool);
		if (bool) {
			boolean destoryCard = iTblAssetCardService.destoryCard(assetId, Longs.tryParse(sellerUserId), amount.intValue());
			log.info("reMintPayAfter next assetId:{},payOrderId:{},bool:{},destoryCard:{}", asset.getId(), payOrderId, bool, destoryCard);
			AssertUtils.isFalse(destoryCard, ErrorMessage.DESTORY_OPERATOR_FAIL);
		}
	}

	private List<MintDataVo> mintAttr(String metadata) {
		if (StringUtils.isNotBlank(metadata)) {
			MetaDataParams bean = JSONUtil.toBean(metadata, MetaDataParams.class);
			List<MintDataVo> orElse = Optional
					.ofNullable(bean)
					.filter(Objects::nonNull)
					.map(MetaDataParams::getAttributes)
					.orElse(Lists.newArrayList())
					.stream()
					.map(x -> new MintDataVo(x.getTrait_type(), String.valueOf(x.getValue())))
					.collect(toList());
			return orElse;
		}
		return Lists.newArrayList();
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public MintResponse reMint(String uid, ReMintRequest mintRequest, OrderTypeEnum orderType) {
		TblAsset asset = iTblAssetService.infoWithBizIdByCache(mintRequest.getGoodsId());
		AssertUtils.isNull(asset, ErrorMessage.ASSET_NOT_EXIST);
		TblAssetExt assetExt = iTblAssetExtService.info(asset.getId());
		AssertUtils.isNull(assetExt, ErrorMessage.ASSET_NOT_EXIST);
		ReferProtocolEnum referProtocol = assetExt.referProtocol();
		AssertUtils.isNull(referProtocol, ErrorMessage.UNSUPPORTED_REFER_PROTOCOL);
		// 先扣能重铸的次数
		accountAdjust(asset.getId(), Longs.tryParse(uid), referProtocol, new BigDecimal("-1"));
		// 新资有没有创建过
		// 查询根据这个模板有没有创建过资产
		// parentId = asset的goodsId
		// author=自己
		String goodsId = asset.getGoodsId();
		TblAsset newAsset = iTblAssetService.infoByParentIdAndSecondType(goodsId, SecondEnum.MINT_POWER.getCode(), uid);
		TblAssetStaticResource assetStatic = iTblAssetStaticResourceService.infoWithAssetIdByCache(asset.getId());
		List<MintDataVo> mintAttr = mintAttr(assetExt.getMetadata());
		AssetTypeEnum assetType = AssetTypeEnum.queryConvert(asset.getFirstType());
		// 如果是7211从0开始
		// 用goodsId维护一个顺序号
		Long parentId = Longs.tryParse(goodsId);
		Long nextSeqNo = 0L;
		if (assetExt.isErc721()) {
			nextSeqNo = iTblAssetService.nextSeqNo(parentId);
		}
		MintRequest requst = MintRequest
				.builder()
				.description(asset.getDescription())
				.externalUrl(assetStatic.getFileUri())
				.image(assetStatic.getDisplayUri())
				.name(asset.getAssetName())
				.attributes(mintAttr)
				.authorId(String.valueOf(uid))
				.referProtocol(referProtocol.getDescription())
				.assetType(assetType.name())
				.bizId(IdUtil.getSnowflakeNextIdStr())
				.chainId(Ints.tryParse(assetExt.getChainId()))
				.amount(1)
				.price(asset.getPrice())
				.unit(asset.getUnit())
				.bizType(BizTypeEnum.NFT.dbType())
				.sellEndDatetime(assetExt.getSellStartDatetime())
				.sellEndDatetime(assetExt.getSellEndDatetime())
				.showState(ShowStateEnum.DOWN.code())
				.ownerType(OwnerTypeEnum.USER.code())
				.parentId(Longs.tryParse(goodsId))
				.secondType(3)
				.seqNo(nextSeqNo)
				.sellable(Objects.isNull(asset.getSellable()) ? StatusAllEnum.NO.code() : asset.getSellable())
				.giftable(Objects.isNull(asset.getGiftable()) ? StatusAllEnum.NO.code() : asset.getGiftable())
				.rankable(Objects.isNull(asset.getRankable()) ? StatusAllEnum.NO.code() : asset.getRankable())
				.rootAssetId(asset.getRootAssetId())
				.build();
		// 扣减能使用的铸造次数
		MintResponse mint = null;
		// 如果是721或者没有铸造过的，直接调用铸造
		if (assetExt.isErc721() || Objects.isNull(newAsset)) {
			mint = iNftService.mint(requst, orderType);
			AssertUtils.isNull(mint, ErrorMessage.REMAINT_OPERATOR_FAIL);
		} else {
			TblAssetExt newAssetExt = iTblAssetExtService.info(newAsset.getId());
			TblAssetTransaction repeatMint = iNftService.repeatMint(Longs.tryParse(uid), newAsset, newAssetExt, orderType);
			AssertUtils.isNull(repeatMint, ErrorMessage.REMAINT_OPERATOR_FAIL);
			mint = iNftService.minResponse(repeatMint.getAssetId(), goodsId, repeatMint.getOrderId(), requst);
			// 基础表的数据+1
			// 清除缓存
			Boolean assetAmountIncr = iTblAssetService.assetAmountIncr(newAsset.getId(), SecondEnum.MINT_POWER.getCode(), uid);
			AssertUtils.isFalse(assetAmountIncr, ErrorMessage.REMAINT_OPERATOR_FAIL);
			cacheManager.delete(AssetCache.TBL_ASSET, newAsset.getGoodsId());
		}
		AssertUtils.isNull(mint, ErrorMessage.REMAINT_OPERATOR_FAIL);
		// 生成卡片
//		boolean genCard = iTblAssetCardService.genCard(mint.getAssetId(), Longs.tryParse(uid));
//		AssertUtils.isFalse(genCard, ErrorMessage.GEN_CARD_FAIL);
		iTblAssetNoService.rMintAssetNo(asset.getId(), Longs.tryParse(uid), mint.getAssetId(),asset.getRootAssetId());
		return mint;
	}

	@Override
	public RemintAccountVo info(Long id) {
		TblRemintAccount remintAccount = baseMapper.selectById(id);
		if (Objects.isNull(remintAccount)) {
			return null;
		}
		// 获取资产信息
		Map<String, AssetVo> assetListByIds = iTblAssetService.getAssetListByIds(new HashSet<>(Arrays.asList(remintAccount.getAssetId())));
		AssetVo assetVo = assetListByIds.get(remintAccount.getAssetId().toString());
		if (Objects.isNull(assetVo)) {
			return null;
		}
		return RemintAccountVo.create(remintAccount, assetVo);
	}

	@Override
	public List<RemintAccountVo> getListByUserId(Long userId, Integer currPage, Integer pageSize) {

		currPage = Optional.ofNullable(currPage).filter(Objects::nonNull).filter(x -> x > 0).orElse(1);
		String page = String.format("limit %d, %d", ((currPage - 1) * pageSize), pageSize);

		LambdaQueryWrapper<TblRemintAccount> query = new LambdaQueryWrapper<>();
		query.eq(TblRemintAccount::getOwnerId, userId).gt(TblRemintAccount::getAmount, 0).orderByDesc(TblRemintAccount::getCreateTime).last(page);
		List<TblRemintAccount> remintAccounts = baseMapper.selectList(query);
		HashSet<Long> assertIds = new HashSet<>();
		remintAccounts.forEach(remintAccount -> {
			assertIds.add(remintAccount.getAssetId());
		});

		Map<String, AssetVo> assetListByIds = iTblAssetService.getAssetListByIds(assertIds);
		List<RemintAccountVo> list = new ArrayList<>();
		remintAccounts.forEach(remintAccount -> {
			AssetVo assetVo = assetListByIds.get(remintAccount.getAssetId().toString());
			if (Objects.nonNull(assetVo)) {
				RemintAccountVo remintAccountVo = RemintAccountVo.create(remintAccount, assetVo);
				list.add(remintAccountVo);
			}
		});
		return list;
	}
}
